package common;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.druid.pool.DruidDataSource;

public class ConnetionPool {
	private final static Log log = LogFactory.getLog(ConnetionPool.class);
	private final static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
	private static DataSource dataSource;
	private static boolean show_sql = false;
	final static ConcurrentHashMap<Long, ConnectionContext> conn_context;

	public static class ConnectionContext {
		public Exception exception;
		public Map<String, String[]> params;
		public String thread;
		public String uri;
		public String ip;

		public ConnectionContext(Exception excp, String ip, String uri,
				Map<String, String[]> params) {
			this.exception = excp;
			this.params = params;
			this.thread = Thread.currentThread().getName();
			this.uri = uri;
			this.ip = ip;
		}

		public Exception getException() {
			return this.exception;
		}

		public Map<String, String[]> getParams() {
			return this.params;
		}

		public String getThread() {
			return this.thread;
		}

		public String getUri() {
			return this.uri;
		}

		public String getIp() {
			return this.ip;
		}
	}

	public static class PoolStatus {
		public int total = -1; // 总连接数
		public int busy = -1; // 活动连接
		public int idle = -1; // 空闲连接
		public Map<Long, ConnectionContext> conns;

		public int getTotal() {
			return total;
		}

		public int getBusy() {
			return busy;
		}

		public int getIdle() {
			return idle;
		}

		public Map<Long, ConnectionContext> getConns() {
			return conns;
		}
	}

	static {
		conn_context = new ConcurrentHashMap<Long, ConnectionContext>();
		initDataSource();
	}

	private static void initDataSource() {
		Properties dbProperties = new Properties();
		try {
			dbProperties.load(ConnetionPool.class
					.getResourceAsStream("db.properties"));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		String dataSrc = dbProperties.getProperty("jdbc.datasource");
		if (dataSrc.indexOf("druid") > 0) {
			try {
				DruidDataSource dataSource = (DruidDataSource) Class.forName(
						dataSrc).newInstance();
				dataSource.setDriverClassName(dbProperties
						.getProperty("jdbc.driver"));
				dataSource.setUsername(dbProperties
						.getProperty("jdbc.username"));
				dataSource.setPassword(dbProperties
						.getProperty("jdbc.password"));
				dataSource.setUrl(dbProperties.getProperty("jdbc.url"));
				dataSource.setInitialSize(Integer.parseInt(dbProperties
						.getProperty("jdbc.initialSize")));
				dataSource.setMinIdle(Integer.parseInt(dbProperties
						.getProperty("jdbc.minIdle")));
				dataSource.setMaxActive(Integer.parseInt(dbProperties
						.getProperty("jdbc.maxActive"))); // 启用监控统计功能
				dataSource
						.setMinEvictableIdleTimeMillis(Integer.parseInt(dbProperties
								.getProperty("jdbc.minEvictableIdleTimeMillis")));
				dataSource.setTestOnBorrow(Boolean.parseBoolean(dbProperties
						.getProperty("jdbc.testOnBorrow")));
				dataSource.setTestWhileIdle(Boolean.parseBoolean(dbProperties
						.getProperty("jdbc.testWhileIdle")));
				dataSource.setValidationQuery(dbProperties
						.getProperty("jdbc.validationQuery"));
				dataSource.setRemoveAbandoned(Boolean.parseBoolean(dbProperties
						.getProperty("jdbc.removeAbandoned")));
				dataSource.setRemoveAbandonedTimeout(Integer
						.parseInt(dbProperties
								.getProperty("jdbc.removeAbandonedTimeout")));
				dataSource.setLogAbandoned(Boolean.parseBoolean(dbProperties
						.getProperty("jdbc.logAbandoned")));
				dataSource.setPoolPreparedStatements(false);
				ConnetionPool.dataSource = dataSource;
//				show_sql=Boolean.parseBoolean(dbProperties
//								.getProperty("jdbc.show_sql"));
				log.info("Using DataSource : "
						+ dataSource.getClass().getName());

				Connection conn = getConnection();
				DatabaseMetaData mdm = conn.getMetaData();
				log.info("Connected to " + mdm.getDatabaseProductName() + " "
						+ mdm.getDatabaseProductVersion());
				closeConnection();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	public static PoolStatus getPoolStatus() {
		PoolStatus pool = new PoolStatus();
		try {
			if (dataSource.getClass().getName().indexOf("c3p0") > 0) {
				pool.total = (Integer) PropertyUtils.getProperty(dataSource,
						"numConnectionsDefaultUser");
				pool.busy = (Integer) PropertyUtils.getProperty(dataSource,
						"numBusyConnectionsDefaultUser");
				pool.idle = (Integer) PropertyUtils.getProperty(dataSource,
						"numIdleConnectionsDefaultUser");
			} else if (dataSource.getClass().getName().indexOf("bonecp") > 0) {
				pool.total = (Integer) PropertyUtils.getProperty(dataSource,
						"maxConnectionsPerPartition")
						* (Integer) PropertyUtils.getProperty(dataSource,
								"partitionCount");
				// pool.busy = (Integer)PropertyUtils.getProperty(dataSource,
				// "numBusyConnectionsDefaultUser");
				// pool.idle = (Integer)PropertyUtils.getProperty(dataSource,
				// "numIdleConnectionsDefaultUser");
			} else if (dataSource.getClass().getName().indexOf("druid") > 0) {
				pool.total = (Integer) PropertyUtils.getProperty(dataSource,
						"maxActive");
				pool.busy = (Integer) PropertyUtils.getProperty(dataSource,
						"activeCount");
				pool.idle = (Integer) PropertyUtils.getProperty(dataSource,
						"poolingCount");
			}
			pool.conns = conn_context;
		} catch (Exception e) {
		}
		return pool;
	}

	/**
	 * 断开连接池
	 */
	public final static void closeDataSource() {
		try {
			dataSource.getClass().getMethod("close").invoke(dataSource);
		} catch (NoSuchMethodException e) {
		} catch (Exception e) {
			log.error("Unabled to destroy DataSource!!! ", e);
		}
	}

	public final static Connection getConnection() throws SQLException {
		Connection conn = conns.get();
		if (conn == null || conn.isClosed()) {
			conn = _getConnection();
			if (conn == null)
				throw new SQLException("Get Connection Fail.");
			conns.set(conn);
		}
//		return (show_sql && !Proxy.isProxyClass(conn.getClass())) ? new _DebugConnection(
//				conn).getConnection() : conn;
		log.info("Get Connection from DataSource Success");
		return conn;
	}

	private static Connection _getConnection() throws SQLException {
		try {
			return dataSource.getConnection();
		} catch (Exception e) {
			log.error("Get Connection from DataSource Fail", e);
			return null;
			/**
			 * return DriverManager.getConnection( cp_props.getProperty("url"),
			 * cp_props.getProperty("username"),
			 * cp_props.getProperty("password"));
			 */
		}
	}

	/**
	 * 关闭连接
	 */
	public final static void closeConnection() {
		Connection conn = conns.get();
		try {
			if (conn != null && !conn.isClosed()) {
				conn.setAutoCommit(true);
				conn.close();
				conn_context.remove(Thread.currentThread().getId());
				log.info("Close DataBase Connection Success! ");
			}
		} catch (SQLException e) {
			log.error("Close DataBase Connection Fail! ", e);
		}
		conns.set(null);
	}

	/**
	 * 用于跟踪执行的SQL语句
	 * 
	 * @author liudong
	 */
//	static class _DebugConnection implements InvocationHandler {
//
//		private final static Log log = LogFactory
//				.getLog(_DebugConnection.class);
//
//		private Connection conn = null;
//
//		public _DebugConnection(Connection conn) {
//			this.conn = conn;
//		}
//
//		/**
//		 * Returns the conn.
//		 * 
//		 * @return Connection
//		 */
//		public Connection getConnection() {
//			return (Connection) Proxy.newProxyInstance(conn.getClass()
//					.getClassLoader(), conn.getClass().getInterfaces(), this);
//		}
//
//		public Object invoke(Object proxy, Method m, Object[] args)
//				throws Throwable {
//			try {
//				String method = m.getName();
//				if ("prepareStatement".equals(method)
//						|| "createStatement".equals(method))
//					log.info("[SQL] >>> " + args[0]);
//				return m.invoke(conn, args);
//			} catch (InvocationTargetException e) {
//				throw e.getTargetException();
//			}
//		}
//
//	}
}